package com.liangdev.common.util;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang.math.RandomUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 自动产生统计信息的thread pool
 * 
 * @author 陈坚 2012-5-9 下午1:42:18 (抄自java concurrency in practice 书中的一个例子)
 */
public class StatsThreadPool extends ThreadPoolExecutor {

    private final static ThreadLocal<Long> startTime = new ThreadLocal<Long>();

    private final static Logger            log       = LoggerFactory.getLogger("THREAD_POOL_STATS_LOGGER");
    private final AtomicLong               numTasks  = new AtomicLong();
    private final AtomicLong               totalTime = new AtomicLong();

    /**
     * 线程池的名字
     */
    private final String                   poolName;

    public static StatsThreadPool newPool(String poolNameSeed, int corePoolSize, int maximumPoolSize,
                                          RejectedExecutionHandler handler) {
        String poolName = poolNameSeed + DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMddHHmmssSSS")
                          + RandomUtils.nextInt(1000);
        StatsThreadPool pool = new StatsThreadPool(poolName, corePoolSize, maximumPoolSize, handler);
        return pool;
    }

    StatsThreadPool(String poolName, int corePoolSize, int maximumPoolSize, RejectedExecutionHandler handler){
        super(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
              new PoolThreadFactory(poolName), handler);
        this.poolName = poolName;
        infoLog("Thread pool made. name = {},  core-pool-size = {} ", new Object[] { poolName, corePoolSize });
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        debugLog("One pooled-thread started. Thread = {}, Pool = {}", new Object[] { t, poolName });
        startTime.set(System.currentTimeMillis());
    }

    @Override
    protected void afterExecute(Runnable r, Throwable e) {
        try {
            long endTime = System.currentTimeMillis();
            long taskTime = endTime - startTime.get();
            numTasks.incrementAndGet();
            totalTime.addAndGet(taskTime);
            debugLog("One pooled-task ended. Pool Name = {},  time={} ms", new Object[] { poolName, taskTime });
        } finally {
            super.afterExecute(r, e);
        }
    }

    @Override
    protected void terminated() {
        try {
            long avgTime = 0;
            if (numTasks.get() != 0) {
                avgTime = totalTime.get() / numTasks.get();
            }

            infoLog("One pool terminated: poolName = {},  total time= {}ms,  numTasks = {}, avg time= {}ms",
                    new Object[] { poolName, totalTime, numTasks, avgTime });
        } finally {
            super.terminated();
        }
    }

    public String getPoolName() {
        return poolName;
    }

    void infoLog(String msg, Object[] argArray) {
        if (log.isInfoEnabled()) {
            log.info(msg, argArray);
        }
    }

    private void debugLog(String msg, Object[] argArray) {
        if (log.isDebugEnabled()) {
            log.debug(msg, argArray);
        }
    }
}
